package org.deyang.tools.amazon;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Properties;
import java.util.logging.Logger;

import javax.mail.Message;
import javax.mail.MessagingException;
import javax.mail.Multipart;
import javax.mail.Session;
import javax.mail.Transport;
import javax.mail.internet.AddressException;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeBodyPart;
import javax.mail.internet.MimeMessage;
import javax.mail.internet.MimeMultipart;
import javax.mail.internet.MimeUtility;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;

import org.deyang.tools.amazon.pojos.PriceInformation;
import org.deyang.tools.amazon.pricetracker.PriceAnalyzer;
import org.deyang.tools.amazon.pricetracker.SimplePriceAnalyzer;
import org.deyang.tools.amazon.utils.AmazonXMLProcessor;
import org.deyang.tools.amazon.utils.GlobalConstants;
import org.deyang.tools.amazon.utils.PriceUtils;
import org.deyang.tools.amazon.utils.SignedRequestsHelper;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;

import com.google.appengine.api.datastore.DatastoreService;
import com.google.appengine.api.datastore.DatastoreServiceFactory;
import com.google.appengine.api.datastore.Entity;
import com.google.appengine.api.datastore.EntityNotFoundException;
import com.google.appengine.api.datastore.Key;
import com.google.appengine.api.datastore.KeyFactory;
import com.google.appengine.api.datastore.Query;
import com.google.appengine.api.datastore.Text;
import com.google.appengine.api.taskqueue.Queue;
import com.google.appengine.api.taskqueue.QueueFactory;
import com.google.appengine.api.taskqueue.TaskOptions;

public class TrackProductsServlet extends HttpServlet {
	
	/**
	 * 
	 */
	private static final long serialVersionUID = 3309246974997066084L;
	private static final Logger log = Logger.getLogger(TrackProductsServlet.class.getName());
	private ArrayList<String> dealsKeys = new ArrayList<String>();
	private HashMap<String,Key> checkedEntitiesASINs = new HashMap<String, Key>();
	//private HashMap<String,Entity> priceListMap = new HashMap<String,Entity>();
	
	private StringBuffer dealsASINStr;

	public void doGet(HttpServletRequest req, HttpServletResponse resp)
	throws IOException {
		//Initial Deals check variants
		dealsKeys.clear();
		checkedEntitiesASINs.clear();
		//priceListMap.clear();
		Date checkDate = new Date();
		dealsASINStr = new StringBuffer();
		
		DatastoreService datastore = DatastoreServiceFactory.getDatastoreService();
		
		Query productq = new Query("AmazonProduct");
		productq.addFilter("trackStatus", Query.FilterOperator.EQUAL, "T");
		
		Iterator<Entity> orginaliter = datastore.prepare(productq).asIterator();
		//Get all results from the query, avoid transaction timeout
		ArrayList<Entity> resultList = new ArrayList<Entity>();
		while (orginaliter.hasNext()){
			resultList.add(orginaliter.next());
		}
		Iterator<Entity> iter = resultList.iterator();
		
		int count = 0;
		HashMap<String, Entity> entityMap = new HashMap<String, Entity>();
		while (iter.hasNext()){
			//because AWS only allow 10 ids in one lookup call. so we need to split these ids
			Entity currentEntity = iter.next();
			String currentasin = (String) currentEntity.getProperty("asin");
			
			if (checkedEntitiesASINs.containsKey(currentasin)){
				//If the asin is already exists in asin set,it is a duplicate record, delete the junior one
				log.warning("This entity maybe duplicate in datastore, delete it: " + currentasin);
				try{
					Entity existEntity = datastore.get(checkedEntitiesASINs.get(currentasin));
					if (((Text)existEntity.getProperty("PriceHistoryData")).getValue().length() > ((Text)currentEntity.getProperty("PriceHistoryData")).getValue().length()){
						datastore.delete(currentEntity.getKey());
					}else{
						datastore.delete(existEntity.getKey());
						//Update hashmap with new entity key value
						checkedEntitiesASINs.put(currentasin, currentEntity.getKey());
					}
					
				} catch (EntityNotFoundException e) {
					e.printStackTrace();
					checkedEntitiesASINs.put(currentasin, currentEntity.getKey());
				}
				
				
				continue;
			}else{
				checkedEntitiesASINs.put(currentasin, currentEntity.getKey());
			}
				
			count++;
			
			entityMap.put(currentasin, currentEntity);
			if (count == 10){
				count = 0;
				check10ProductsPrice(entityMap, datastore, checkDate);
				entityMap.clear();
			}
		}
		if (entityMap.size() > 0){
			check10ProductsPrice(entityMap, datastore, checkDate);
		}
		
		//Send mail
		sendDealNotifyMail(datastore);
		
		//Write track status to db
		Entity trackSummary = new Entity("TrackSummary");
		trackSummary.setProperty("checkDate", checkDate);
		trackSummary.setProperty("findDealsCount", dealsKeys.size());
		trackSummary.setProperty("findDealsASIN", new Text(dealsASINStr.toString()));
		trackSummary.setProperty("checkedProductsCount", checkedEntitiesASINs.size());
		datastore.put(trackSummary);
	}
	
	private void sendDealNotifyMail(DatastoreService datastore){
		Properties props = new Properties();
        Session session = Session.getDefaultInstance(props, null);

        StringBuffer htmlBody = new StringBuffer();
        for (int i = 0; i < dealsKeys.size(); i++){
        	try {
				Entity currentEntity = datastore.get(KeyFactory.stringToKey(dealsKeys.get(i)));
				String urlStr = "";
				Object urlObject = currentEntity.getProperty("shorturl");
				if (urlObject instanceof Text){
					urlStr = ((Text)urlObject).getValue();
				}else{
					urlStr = (String)urlObject;
				}
				htmlBody.append("<img src=\"");
				htmlBody.append(currentEntity.getProperty("imgurl"));
				htmlBody.append("\" />&nbsp;");
				htmlBody.append(currentEntity.getProperty("dealPrice"));
				htmlBody.append("&nbsp;<span>");
				htmlBody.append(currentEntity.getProperty("amazonProductTitle"));
				htmlBody.append("</span>  <a href=\"");
				htmlBody.append(urlStr);
				htmlBody.append("\">Product Link</a> <br>");
				
				dealsASINStr.append(currentEntity.getProperty("asin"));
				dealsASINStr.append("|");
				//currentEntity.setProperty("isDealForNotify", false);
				//datastore.put(currentEntity);
			} catch (EntityNotFoundException e) {
				continue;
			}
        }
        
        
        try {
            Message msg = new MimeMessage(session);
            msg.setFrom(new InternetAddress("liudeyang@gmail.com", "aaa search engine admin"));
            msg.addRecipient(Message.RecipientType.TO,
                             new InternetAddress("liudeyang@gmail.com", "Deyang Liu"));
            
            Multipart mp = new MimeMultipart();

            MimeBodyPart htmlPart = new MimeBodyPart();
            htmlPart.setContent(htmlBody.toString(), "text/html");
            mp.addBodyPart(htmlPart);
            msg.setSubject(MimeUtility.encodeText("美国亚马逊报告:发现" + dealsKeys.size() + "个产品优惠信息", "UTF-8", null));
            msg.setContent(mp);
            Transport.send(msg);

        } catch (AddressException e) {
        	log.severe(e.getMessage());
        } catch (MessagingException e) {
        	log.severe(e.getMessage());
        } catch (UnsupportedEncodingException e) {
			log.severe(e.getMessage());
		}
	}
	
	private void check10ProductsPrice(HashMap<String, Entity> entityMap, DatastoreService datastore, Date checkDate){
		/*
         * Set up the signed requests helper 
         */
        SignedRequestsHelper helper = null;
        try {
            helper = SignedRequestsHelper.getInstance(GlobalConstants.ENDPOINT, GlobalConstants.AWS_ACCESS_KEY_ID, GlobalConstants.AWS_SECRET_KEY);
        } catch (Exception e) {
            e.printStackTrace();
            return;
        }
        
        //Create item ids string for AWS
        StringBuffer sb = new StringBuffer();
        Iterator<String> keys = entityMap.keySet().iterator();
        while (keys.hasNext()){
        	if (sb.length() == 0){
        		sb.append(keys.next());
        	}else{
        		sb.append(",");
        		sb.append(keys.next());
        	}
        }
        Map<String, String> params = new HashMap<String, String>();
        params.put("Service", GlobalConstants.SERVICE_NAME);
        params.put("AssociateTag", GlobalConstants.ASSOCIATE_TAG);
        params.put("Operation", "ItemLookup");
        params.put("ItemId", sb.toString());
        //params.put("Condition", "New");
        params.put("Availability", "Available");
        params.put("MerchantId", "Amazon");
        params.put("ResponseGroup", "Medium,Offers,OfferFull");
        
        String requestUrl = helper.sign(params);
        
        try {
			URL url = new URL(requestUrl);
			
			DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
        	DocumentBuilder db = dbf.newDocumentBuilder();
        	org.w3c.dom.Document domDoc = db.parse(url.openStream());
        	NodeList itemList = domDoc.getElementsByTagName("Item");
        	
        	for (int i = 0; i < itemList.getLength(); i++){
        		Element itemElement = (Element) itemList.item(i);
        		String itemFormat = AmazonXMLProcessor.getDefaultChildNodeValue(AmazonXMLProcessor.getDefaultChildNode(itemElement, "ItemAttributes"),"Format");
        		String itemBinding = AmazonXMLProcessor.getDefaultChildNodeValue(AmazonXMLProcessor.getDefaultChildNode(itemElement, "ItemAttributes"),"Binding");
        		String listPriceAmount = AmazonXMLProcessor.getDefaultChildNodeValue(AmazonXMLProcessor.getDefaultChildNode(AmazonXMLProcessor.getDefaultChildNode(itemElement, "ItemAttributes"), "ListPrice"),"Amount");
        		PriceInformation pi = PriceUtils.getCurrentLowPrice(itemElement, true);
        		String currentEntityasin = AmazonXMLProcessor.getDefaultChildNodeValue((Element) itemElement, "ASIN");
        		Entity currentEntity = entityMap.get(currentEntityasin);
        		String currentKey = KeyFactory.keyToString(currentEntity.getKey());
        		String oldDealPrice = (String) currentEntity.getProperty("dealPrice");
        		PriceAnalyzer pa;
        		
    			boolean noExistingPriceHistory = false;
				String priceListStr;
				if (currentEntity.getProperty("PriceHistoryData") == null || ((Text)currentEntity.getProperty("PriceHistoryData")).getValue().equals("")){
					noExistingPriceHistory = true;
					priceListStr = "";
				}else{
					priceListStr = ((Text) currentEntity.getProperty("PriceHistoryData")).getValue();
					if (priceListStr.startsWith(",")){
						priceListStr = priceListStr.substring(1);
					}
					if (priceListStr.endsWith(",")){
						priceListStr = priceListStr.substring(0,priceListStr.length() - 1);
					}
				}
				
				if ((pi.isAmazonPriceAvailable() && noExistingPriceHistory) 
						|| (pi.isAmazonPriceAvailable() && !pi.getAmazonPrice().equals(oldDealPrice))){
					long hoursNumber = Math.round((Calendar.getInstance().getTimeInMillis() - GlobalConstants.KEEPASTARTDATE.getTimeInMillis()) / GlobalConstants.HOUR_MILLIS);
					
					//Update price history data and write it back to datastore
					if (noExistingPriceHistory){
						priceListStr = "" + hoursNumber + "," + pi.getAmazonPriceAmount();
					}else{
						//Only record prich value which is different as existing recorded price history
						if (!pi.getAmazonPriceAmount().equals(priceListStr.substring(priceListStr.lastIndexOf(",") + 1))){
							priceListStr = priceListStr + "," + hoursNumber + "," + pi.getAmazonPriceAmount();	
						}
					}
					currentEntity.setProperty("PriceHistoryData" , new Text(priceListStr));
					
					//Price changed, write it back to db
					currentEntity.setProperty("dealPrice", pi.getAmazonPrice());
					datastore.put(currentEntity);

					pa = new SimplePriceAnalyzer(currentEntityasin,priceListStr);
					if (pa.isDeal(30)){
						dealsKeys.add(currentKey);
						Queue queue = QueueFactory.getQueue("twitterpostqueue");
						
						//Prepare additonal header text for Tweet
						String additionalHeader = "";
						if (pa.getDealDetail().isFreeContent()){
							additionalHeader = "#Free ";
						}else{
							additionalHeader = pi.getSavedPercentage(listPriceAmount);
						}
						if (itemFormat.equals("Kindle eBook")){
							additionalHeader = additionalHeader + "#KindleDailyDeal ";
						}
						if (itemBinding.equals("Amazon Instant Video")){
							additionalHeader = additionalHeader + "#AmazonVideo ";
						}
						
						queue.add(TaskOptions.Builder.withUrl("/twitterpost").param("key", currentKey).param("additionalheader", additionalHeader));
					}
					
//					//ebook and so on will not publish to weibo
//					if (isPostToWeibo && !ASINUtils.isValidISBN(currentEntityasin)){
//						currentEntity.setProperty("isPendingToWeibo", isPostToWeibo);
//					}
					
					
				}else if (!pi.isAmazonPriceAvailable()){
					//If this product has no amazon price, then delete it
					log.warning("The asin which should be delete is :" + currentEntityasin);
					datastore.delete(currentEntity.getKey());
				}
        		
        	}
        }catch(Exception e){
        	log.severe(e.getMessage());
        }
	}

}
